package com.springboot.frame.comm;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.persistence.Id;
import com.springboot.frame.comm.database.registry.MysqlTypeRegistry;
import com.springboot.frame.comm.domain.Column;
import com.springboot.frame.comm.domain.FileTemplate;
import com.springboot.frame.comm.domain.Table;
import com.springboot.frame.comm.domain.Template;
import com.springboot.frame.comm.exception.CodeGeneratorException;
import com.springboot.frame.comm.render.FreemarkerRender;
import org.apache.commons.lang3.StringUtils;
import static com.springboot.frame.comm.AutoCodeGenerator.mkDir;

/**
 * 实体到数据库
 *
 * @author liheng
 */
public class AutoCodeReverser {

  public void generate(String basePackage) {
    Map<String, Object> readerData = new HashMap<>(1);
    List<Class<?>> classList = findClass(basePackage);
    for (Table table : generateTable(classList)) {
      readerData.put("table", table);
      FileTemplate template = table.getFileTemplates().values().iterator().next();
      File file = new File(template.getFilePath());
      if (!file.exists()) {
        try (BufferedWriter writer = new BufferedWriter(new FileWriter(file))) {
          template.render(readerData, writer);
        } catch (IOException e) {
          e.printStackTrace();
        }
      }
    }
  }

  /**
   * Class生成Table
   */
  private List<Table> generateTable(List<Class<?>> classList) {
    MysqlTypeRegistry typeRegistry = MysqlTypeRegistry.INSTANCE;
    List<Table> tableList = new ArrayList<>();
    for (Class<?> clazz : classList) {
      Table table = new Table();
      table.setJdbcName(clazz.getSimpleName().toLowerCase());
      List<Column> columnList = new ArrayList<>();
      for (Field field : clazz.getDeclaredFields()) {
        if (Modifier.isStatic(field.getModifiers())) {
          continue;
        }
        Column column = new Column();
        Id id = field.getAnnotation(Id.class);
        if (id != null || StringUtils.equalsAnyIgnoreCase(field.getName(),
            "id", (field.getName() + "id"))) {
          column.setIsPrimaryKey(true);
        }
        column.setJdbcName(javaNameToJdbcName(field.getName()));
        column.setJdbcType(typeRegistry.getJdbcType(field.getType()));
        columnList.add(column);
        table.setColumns(columnList);
      }
      Template template = new Template("sql",
          new FreemarkerRender("entity.sql.ftl"), "%s", "%s", ".sql");
      String filePackage = String.format(template.getOutFilePackage(), clazz.getName());
      String fileDir = "E:\\" + filePackage.replaceAll("\\.", "\\" + File.separator);
      mkDir(fileDir);
      String fileName = String
          .format(template.getOutFilename(), clazz.getSimpleName().toLowerCase());
      String filePath = fileDir + File.separator + fileName + template.getOutFileType();
      Map<String, FileTemplate> map = new HashMap<>(1);
      map.put(template.getName(),
          new FileTemplate(template.getRender(), fileName, filePackage, filePath));
      table.setFileTemplates(map);
      tableList.add(table);
    }
    return tableList;
  }

  /**
   * 获得Classes
   */
  public List<Class<?>> findClass(String packageName) {
    List<Class<?>> classList = new ArrayList<>();
    String packageDir = packageName.replace('.', '/');
    try {
      Enumeration<URL> dirs = Thread.currentThread().getContextClassLoader()
          .getResources(packageDir);
      while (dirs.hasMoreElements()) {
        URL url = dirs.nextElement();
        File dirFile = new File(url.getFile());
        File[] files = dirFile.listFiles();
        if (files != null) {
          for (File file : files) {
            if (file.isFile() && StringUtils.endsWith(file.getName(), ".class")) {
              String className = StringUtils.remove(file.getName(), ".class");
              Class<?> clazz = Class.forName(packageName + "." + className);
              classList.add(clazz);
            }
          }
        }
      }
    } catch (Exception e) {
      throw new CodeGeneratorException(e);
    }
    return classList;
  }

  private String javaNameToJdbcName(String javaName) {
    if (StringUtils.isEmpty(javaName)) {
      return javaName;
    }
    StringBuilder sb = new StringBuilder();
    char[] chars = javaName.toCharArray();
    sb.append(Character.toLowerCase(chars[0]));
    for (int i = 1; i < chars.length; i++) {
      char c = chars[i];
      if (Character.isUpperCase(c)) {
        sb.append("_").append(Character.toLowerCase(c));
      } else {
        sb.append(c);
      }
    }
    return sb.toString();
  }

}

